/*******************************************************************************
 * Copyright 2010 DemandSpot
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.demandspot.client.table;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import com.demandspot.client.PopupFactory;
import com.demandspot.client.table.DataScrollTable.FooterWidgetLocation;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.gen2.table.client.FixedWidthFlexTable;
import com.google.gwt.gen2.table.client.ScrollTable;
import com.google.gwt.gen2.table.override.client.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DecoratedPopupPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;

public class FilterWidget<RowModel> extends Button {
	private static final Labels LABELS = Labels.Util.getLabels();
	
	private DataScrollTable<FilterRow<RowModel>> scrollTable;
	private DecoratedPopupPanel popupWindow;
	private List<FilterOperation[]> fieldOperationsList = new ArrayList<FilterOperation[]>();
	private List<CellRenderer<RowModel, ?>> cellRenderers = new ArrayList<CellRenderer<RowModel,?>>();
	private ListBox fieldsListBox = new ListBox();
	private ListBox operationsListBox = new ListBox();

	private TextBox valueTextBox;

	/**
	 * Data table to apply filters on
	 */
	private DataTable<RowModel> dataTable;
	
	public FilterWidget(List<CellRenderer<RowModel, ?>> cellRenderers, DataTable<RowModel> dataTable) {
		this.dataTable = dataTable;
		this.cellRenderers = cellRenderers;
		
		setText(LABELS.filterBtnText());
		
		for (Iterator<CellRenderer<RowModel, ?>> iter = cellRenderers.iterator(); iter.hasNext();) {
			CellRenderer<RowModel, ?> cellRenderer = iter.next();
			FilterOperation[] filterOperations = cellRenderer.getFilterOperations();
			if (filterOperations != null && filterOperations.length > 0) {
				Arrays.sort(filterOperations);
				fieldOperationsList.add(filterOperations);
				fieldsListBox.addItem(cellRenderer.getName());
			} else {
				iter.remove();
			}
		}
		
		buildPopupWindow();
		setFilterOperations();
		
		addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent evt) {
				popupWindow.center();
				popupWindow.show();
			}			
		});
	}
	
	private void buildPopupWindow() {
		createScrollTable();
		FlexTable filtersSettingsBody = new FlexTable();
		filtersSettingsBody.setHTML(0, 0, "<b>" + LABELS.field() + ":</b>" );
		filtersSettingsBody.setHTML(0, 1, "<b>" + LABELS.condition() + ":</b>" );
		filtersSettingsBody.setHTML(0, 2, "<b>" + LABELS.value() + ":</b>" );
		
		filtersSettingsBody.setWidget(1, 0, fieldsListBox);
		filtersSettingsBody.setWidget(1, 1, operationsListBox);
		
		operationsListBox.setWidth("85px");
		
		valueTextBox = new TextBox();
		valueTextBox.setWidth("250px");
		filtersSettingsBody.setWidget(1, 2, valueTextBox);
		
		Button addButton = new Button(LABELS.add());
		
		addButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent evt) {
				String value = valueTextBox.getText();
				if (value != null && !value.isEmpty()) {
					FilterOperation filterOperation = FilterOperation.valueOf(operationsListBox.getValue(operationsListBox.getSelectedIndex()));
					CellRenderer<RowModel, ?> field = cellRenderers.get(fieldsListBox.getSelectedIndex());
					FilterRow<RowModel> row = new FilterRow<RowModel>(field, filterOperation, value);
					scrollTable.addModel(row);
				}				
			}			
		});
		
		filtersSettingsBody.setWidget(1, 3, addButton);
		
		fieldsListBox.addChangeHandler(new ChangeHandler() {			
			public void onChange(ChangeEvent evt) {
				setFilterOperations();				
			}			
		});
		
		VerticalPanel popupBody = new VerticalPanel();
		popupBody.setSpacing(3);
		
		popupBody.add(filtersSettingsBody);
		popupBody.add(scrollTable);
		
				
		popupWindow = PopupFactory.showOkCancel(LABELS.tableFilters(), popupBody, true, LABELS.apply(), new Command() {
			public void execute() {
				dataTable.applyFilters(new HashSet<FilterRow<RowModel>>(scrollTable.getAllModels()), true);
			}			
		});
	}
	
	private void createScrollTable() {
		List<CellRenderer<FilterRow<RowModel>,?>> renderers = createRenderers();
		
		FixedWidthFlexTable headerTable = new FixedWidthFlexTable();
	    FlexCellFormatter headerFormatter = headerTable.getFlexCellFormatter();
	    final CheckBox selectAll = new CheckBox();
		headerTable.setWidget(0, 0, selectAll);
	    headerFormatter.setHorizontalAlignment(0, 0, HasHorizontalAlignment.ALIGN_CENTER);
	    headerTable.setHTML(0, 1, LABELS.field());
	    headerTable.setHTML(0, 2, LABELS.condition());
	    headerTable.setHTML(0, 3, LABELS.value());
	    
	    for (int i=0; i < renderers.size(); i++) {
	    	headerFormatter.setStyleName(0, i+1, "sortable");
	    }
		
		scrollTable = DataScrollTable.createTable(new ArrayList<FilterRow<RowModel>>(), renderers, headerTable);
		
		selectAll.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent arg0) {
				if (selectAll.getValue()) {
					scrollTable.selectAllRows();
				} else {
					scrollTable.deselectAllRows();
				}				
			}			
		});
		
	    scrollTable.setCellPadding(3);
	    scrollTable.setCellSpacing(0);
	    scrollTable.setResizePolicy(ScrollTable.ResizePolicy.FILL_WIDTH);
	    
	    scrollTable.setWidth("540px");
	    scrollTable.setHeight("200px");
	    
	    Button removeButton = new Button(LABELS.remove());
	    removeButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent evt) {
				List<FilterRow<RowModel>> models = scrollTable.getSelectedModels();
				
				for (FilterRow<RowModel> filterRow : models) {
					scrollTable.removeModel(filterRow);
				}				
			}	    	
	    });
	    
	    scrollTable.addWidgetToFooter(removeButton, FooterWidgetLocation.RIGHT);
	}
	
	private List<CellRenderer<FilterRow<RowModel>, ?>> createRenderers() {
		List<CellRenderer<FilterRow<RowModel>, ?>> toReturn = new ArrayList<CellRenderer<FilterRow<RowModel>, ?>>();
		
		{
			CellRenderer<FilterRow<RowModel>, String> renderer = new CellRenderer<FilterRow<RowModel>, String>() {
				public String getDisplayedString(FilterRow<RowModel> rowModel) {
					return rowModel.getField().getName();
				}
				
				public String getCompareValue(FilterRow<RowModel> rowModel) {
					return getDisplayedString(rowModel);
				}
				
				public boolean isSortable() {
					return true;
				}
			};
			
			toReturn.add(renderer);
		}
		
		{
			CellRenderer<FilterRow<RowModel>, String> renderer = new CellRenderer<FilterRow<RowModel>, String>() {
				public String getDisplayedString(FilterRow<RowModel> rowModel) {
					return rowModel.getFilterOperation().htmlString();
				}
				
				public String getCompareValue(FilterRow<RowModel> rowModel) {
					return getDisplayedString(rowModel);
				}
				
				public HorizontalAlignmentConstant getHorizontalAlignment() {
					return HasAlignment.ALIGN_CENTER;
				}
				
				public boolean isHTML() {
					return true;
				}
			};
			
			toReturn.add(renderer);
		}
		
		{
			CellRenderer<FilterRow<RowModel>, String> renderer = new CellRenderer<FilterRow<RowModel>, String>() {
				public String getDisplayedString(FilterRow<RowModel> rowModel) {
					return rowModel.getValue();
				}
				
				public String getCompareValue(FilterRow<RowModel> rowModel) {
					return getDisplayedString(rowModel);
				}
				
				public boolean isSortable() {
					return true;
				}
			};
			
			toReturn.add(renderer);
		}
		
		return toReturn;
	}

	private void setFilterOperations() {
		int index = fieldsListBox.getSelectedIndex();
		FilterOperation[] filterOperations = fieldOperationsList.get(index);
		
		operationsListBox.clear();
		
		for (FilterOperation filterOperation : filterOperations) {
			operationsListBox.addItem(filterOperation.htmlString(), filterOperation.name());
		}
	}
}
